package com.jinglin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jinglin.component.RedisComponent;
import com.jinglin.entity.config.AppConfig;
import com.jinglin.entity.constants.Constants;
import com.jinglin.entity.dto.UploadingFileDto;
import com.jinglin.entity.enums.VideoFileTransferResultEnum;
import com.jinglin.entity.enums.VideoStatusEnum;
import com.jinglin.entity.po.VideoInfoFilePost;
import com.jinglin.entity.po.VideoInfoPost;
import com.jinglin.exception.BusinessException;
import com.jinglin.mapper.VideoInfoFilePostMapper;
import com.jinglin.service.VideoInfoFilePostService;
import com.jinglin.service.VideoInfoPostService;
import com.jinglin.utils.FFmpegUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.List;

/**
 * <p>
 * 视频文件信息 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2025-03-12
 */
@Slf4j
@Service
public class VideoInfoFilePostServiceImpl extends ServiceImpl<VideoInfoFilePostMapper, VideoInfoFilePost> implements VideoInfoFilePostService {
    @Autowired
    private RedisComponent redisComponent;
    @Autowired
    private AppConfig appConfig;
    @Autowired
    private FFmpegUtils fFmpegUtils;
    @Autowired
    private VideoInfoFilePostMapper videoInfoFilePostMapper;
    @Resource
    private VideoInfoPostService videoInfoPostService;
    @Override
    @Transactional
    public void transferVideoFile(VideoInfoFilePost videoInfoFilePost) {
        //  创建一个新的videoInfoFilePost对象，用来更新文件的信息
        VideoInfoFilePost updateFileInfoPost = new VideoInfoFilePost();
        try {
            // 根据消息队列中的拿稿件视频文件信息，取出
            UploadingFileDto uploadFileDto = redisComponent.getUploadFileDto(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());
            // 构建临时文件夹路径(这个目录中是一些切片的视频文件)
            String tempFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_FOLDER_TEMP + uploadFileDto.getFilePath();
            File tempFile = new File(tempFilePath);
            // 构建目标文件夹路径
            String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + uploadFileDto.getFilePath();
            File targetFile = new File(targetFilePath);
            if (!targetFile.exists()){
                targetFile.mkdirs();
            }
            // 将临时目录中的切片文件拷贝到目标目录中去
            FileUtils.copyDirectory(tempFile,targetFile);

            // 删除临时文件夹
            FileUtils.deleteDirectory(tempFile);
            // 从Redis中删除视频预上传保存的信息。
            redisComponent.delVideoFileInfo(videoInfoFilePost.getUserId(),videoInfoFilePost.getUploadId());

            /*
            * 将块文件合并
            * */
            String completeVideo = targetFilePath +"/" +Constants.FILE_VIDEO_NAME;
            // 三个参数：文件夹、合并文件后的路径、是否删除源文件
            this.union(targetFilePath,completeVideo,true);

            /*
            * 获取视频播放时长。使用ffmpeg相关命令
            * */
            Integer duration = fFmpegUtils.getVideoInfoDuration(completeVideo);
            updateFileInfoPost.setDuration(duration);
            updateFileInfoPost.setFileSize(new File(completeVideo).length());
            updateFileInfoPost.setFilePath(Constants.FILE_VIDEO + uploadFileDto.getFilePath());
            updateFileInfoPost.setTransferResult(VideoFileTransferResultEnum.SUCCESS.getStatus());

            /*
            * 调用ffmpeg将视频文件转换为ts格式
            * */
            this.convertVideo2Ts(completeVideo);
        }catch (Exception e){
            log.error("视频转码失败",e);
            updateFileInfoPost.setTransferResult(VideoFileTransferResultEnum.FAIL.getStatus());
        }finally {
            // 更新视频文件信息。
            LambdaQueryWrapper<VideoInfoFilePost> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(VideoInfoFilePost::getUserId,videoInfoFilePost.getUserId());
            wrapper.eq(VideoInfoFilePost::getUploadId,videoInfoFilePost.getUploadId());
            this.update(updateFileInfoPost,wrapper);
             //===================== 更新视频信息 =====================
            LambdaQueryWrapper<VideoInfoFilePost> fileWrapper1 = new LambdaQueryWrapper<>();
            fileWrapper1.eq(VideoInfoFilePost::getVideoId,videoInfoFilePost.getVideoId());
            fileWrapper1.eq(VideoInfoFilePost::getTransferResult,VideoFileTransferResultEnum.FAIL.getStatus());
            List<VideoInfoFilePost> list = this.list(fileWrapper1);
            // 如果有视频转码失败了，则给视频集的的状态设置为 转码失败
            if (list.size() > 0){
                LambdaUpdateWrapper<VideoInfoPost> videoInfoPostWrapper = new LambdaUpdateWrapper<>();
                videoInfoPostWrapper.eq(VideoInfoPost::getVideoId,videoInfoFilePost.getVideoId());
                videoInfoPostWrapper.set(VideoInfoPost::getStatus, VideoStatusEnum.STATUS1.getStatus());
                videoInfoPostService.update(videoInfoPostWrapper);
            }


            LambdaQueryWrapper<VideoInfoFilePost> fileWrapper2 = new LambdaQueryWrapper<>();
            fileWrapper2.eq(VideoInfoFilePost::getVideoId,videoInfoFilePost.getVideoId());
            fileWrapper2.eq(VideoInfoFilePost::getTransferResult,VideoFileTransferResultEnum.TRANSFER.getStatus());
            List<VideoInfoFilePost> newList = this.list(fileWrapper2);
            if (newList.size() == 0){
                // 如果没有传输中的文件，计算视频集总时长并更新视频状态为status2 待审核
                Integer sumDuration = videoInfoFilePostMapper.sumDuration(videoInfoFilePost.getVideoId());
                LambdaUpdateWrapper<VideoInfoPost> videoInfoPostWrapper = new LambdaUpdateWrapper<>();
                videoInfoPostWrapper.eq(VideoInfoPost::getVideoId,videoInfoFilePost.getVideoId());
                videoInfoPostWrapper.set(VideoInfoPost::getStatus, VideoStatusEnum.STATUS2.getStatus());
                videoInfoPostWrapper.set(VideoInfoPost::getDuration,sumDuration);
                videoInfoPostService.update(videoInfoPostWrapper);
            }
        }

    }


    // 合并文件的方法
    public static void union(String dirPath, String toFilePath, boolean delSource) throws BusinessException {
        // 检查目录是否存在
        File dir = new File(dirPath);
        if (!dir.exists()) {
            throw new BusinessException("目录不存在");
        }
        // 获取目录下的所有文件
        File fileList[] = dir.listFiles();
        File targetFile = new File(toFilePath);
        // 使用RandomAccessFile进行文件写入
        try (RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw")) {
            byte[] b = new byte[1024 * 10];
            for (int i = 0; i < fileList.length; i++) {
                int len = -1;
                // 创建读块文件的对象
                File chunkFile = new File(dirPath + File.separator + i);
                RandomAccessFile readFile = null;
                try {
                    readFile = new RandomAccessFile(chunkFile, "r");
                    // 读取文件内容并写入目标文件
                    while ((len = readFile.read(b)) != -1) {
                        writeFile.write(b, 0, len);
                    }
                } catch (Exception e) {
                    log.error("合并分片失败", e);
                    throw new BusinessException("合并文件失败");
                } finally {
                    // 关闭读取文件流
                    readFile.close();
                }
            }
        } catch (Exception e) {
            throw new BusinessException("合并文件" + dirPath + "出错了");
        } finally {
            // 如果需要删除源文件，则删除
            if (delSource) {
                for (int i = 0; i < fileList.length; i++) {
                    fileList[i].delete();
                }
            }
        }
    }


    // 将视频文件转换为TS格式的方法
    private void convertVideo2Ts(String videoFilePath) {
        File videoFile = new File(videoFilePath);
        // 创建同名切片目录
        File tsFolder = videoFile.getParentFile();
        // 获取视频编码格式
        String codec = fFmpegUtils.getVideoCodec(videoFilePath);
        // 如果视频编码为HEVC，则转换为MP4格式
        if (Constants.VIDEO_CODE_HEVC.equals(codec)) {
            String tempFileName = videoFilePath + Constants.VIDEO_CODE_TEMP_FILE_SUFFIX;
            new File(videoFilePath).renameTo(new File(tempFileName));
            fFmpegUtils.convertHevc2Mp4(tempFileName, videoFilePath);
            new File(tempFileName).delete();
        }

        // 将视频文件转换为TS格式
        fFmpegUtils.convertVideo2Ts(tsFolder, videoFilePath);

        // 删除原始视频文件
        videoFile.delete();
    }


}
